#region Microsoft Public License
/*
 * (c) Copyright Chenjun Wu. http://utablesdk.codeplex.com
 * 
 * This source is subject to the Microsoft Public License (Ms-PL).
 * Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
 * All other rights reserved.
 */
#endregion

﻿using System;
using System.Collections.ObjectModel;
using System.Windows.Controls;
using TUIO;
using UTable.Input.MultiTouch;
using System.Windows;

namespace UTable.Input
{
	public class TuioProvider: Grid, IInputProvider, TuioListener
	{
        private int _port = 3333;
        private int _id = 0;

        public TuioProvider(int port, int id)
        {
            _port = port;
            _id = id;
            width = System.Windows.SystemParameters.PrimaryScreenWidth;
            height = System.Windows.SystemParameters.PrimaryScreenHeight;
        }

        public void Initialize(UTable.Objects.UApplication context)
        {
            StartTuioClient(Port);
        }

        public int DeviceId
        {
            get { return _id; }
            private set { _id = value; }
        }

        public int Port
        {
            get { return _port; }
            private set { _port = value; }
        }

        public TuioProvider()
        {
            width = System.Windows.SystemParameters.PrimaryScreenWidth;
            height = System.Windows.SystemParameters.PrimaryScreenHeight;
		}

		void Current_Exit(object sender, ExitEventArgs e)
		{
			if (client != null)
			{
				Console.WriteLine("Disconnect TUIO server");
				client.disconnect();
			}
		}

		private void StartTuioClient(int port)
		{
            client = new TuioClient(port);
            client.addTuioListener(this);
            client.connect();
			Application.Current.Exit += new ExitEventHandler(Current_Exit);
            Console.WriteLine("listening to TUIO messages at port " + port);
        }

		private TuioClient client;
        private double width;
        private double height;
        private Collection<FingerEventArgs> fingerEvents = new Collection<FingerEventArgs>();

        private delegate void MultiTouchEventHandler(MultiTouchEventArgs args);
        /// <summary>
        /// Trigger the MultiTouch Event
        /// </summary>
        private void OnMultiTouch(MultiTouchEventArgs args)
        {
            if (InputTriggered != null)
                InputTriggered(args);
        }

        public event InputEventHandler InputTriggered;

		#region implement TuioListener methods

        public void addTuioObject(TuioObject tobj)
        {
            //Console.WriteLine("add obj " + tobj.getSymbolID() + " " + tobj.getSessionID() + " " + tobj.getX() + " " + tobj.getY() + " " + tobj.getAngle());

            FingerEventArgs finger = new FingerEventArgs();
            finger.EventType = FingerEventType.FINGER_DOWN_EVENT;
            finger.FingerID = (int)tobj.getSessionID();
            finger.Position.X = tobj.getX() * width;
            finger.Position.Y = tobj.getY() * height;
			finger.GlobalPosition = finger.Position;
            finger.SourceID = _id;
            finger.Orientation = tobj.getAngle();
            //TODO
            // Set the user ID according to s_id
            // now everyone is super user
            finger.UserID = tobj.getSymbolID();
            finger.DeviceName = this.GetType().ToString();

            fingerEvents.Add(finger);
        }

        public void updateTuioObject(TuioObject tobj)
        {
            //Console.WriteLine("set obj " + tobj.getSymbolID() + " " + tobj.getSessionID() + " " + tobj.getX() + " " + tobj.getY() + " " + tobj.getAngle() + " " + tobj.getMotionSpeed() + " " + tobj.getRotationSpeed() + " " + tobj.getMotionAccel() + " " + tobj.getRotationAccel());

            FingerEventArgs finger = new FingerEventArgs();
            finger.EventType = FingerEventType.FINGER_MOVE_EVENT;
            finger.FingerID = (int)tobj.getSessionID();
            finger.Position.X = tobj.getX() * width;
            finger.Position.Y = tobj.getY() * height;
			finger.GlobalPosition = finger.Position;
            finger.SourceID = _id;
            finger.UserID = tobj.getSymbolID();
            finger.DeviceName = this.GetType().ToString();
            finger.Orientation = tobj.getAngle();

            fingerEvents.Add(finger);
        }

        public void removeTuioObject(TuioObject tobj)
        {
            //Console.WriteLine("del obj " + tobj.getSymbolID() + " " + tobj.getSessionID());

            FingerEventArgs finger = new FingerEventArgs();
            finger.EventType = FingerEventType.FINGER_UP_EVENT;
            finger.FingerID = (int)tobj.getSessionID();
            finger.Position.X = tobj.getX() * width;
            finger.Position.Y = tobj.getY() * height;
            finger.SourceID = _id;
            finger.UserID = tobj.getSymbolID();
            finger.DeviceName = this.GetType().ToString();
            finger.Orientation = tobj.getAngle();

            fingerEvents.Add(finger);
        }

        public void addTuioCursor(TuioCursor tcur)
        {
            //Console.WriteLine("add cur " + tcur.getCursorID() + " (" + tcur.getSessionID() + ") " + tcur.getX() + " " + tcur.getY());


            if (!PreviewCursor(tcur))
                return;

            FingerEventArgs finger = new FingerEventArgs();
            finger.EventType = FingerEventType.FINGER_DOWN_EVENT;
            finger.FingerID = (int)tcur.getSessionID();
            finger.Position.X = tcur.getX() * width;
            finger.Position.Y = tcur.getY() * height;
            //TODO
            // Set the user ID according to s_id
            // now everyone is super user
            finger.UserID = tcur.getCursorID();
            finger.DeviceName = this.GetType().ToString();

            fingerEvents.Add(finger);
        }

        public void updateTuioCursor(TuioCursor tcur)
        {
            //Console.WriteLine("set cur " + tcur.getCursorID() + " (" + tcur.getSessionID() + ") " + tcur.getX() + " " + tcur.getY() + " " + tcur.getMotionSpeed() + " " + tcur.getMotionAccel());

            if (!PreviewCursor(tcur))
                return;

            FingerEventArgs finger = new FingerEventArgs();
            finger.EventType = FingerEventType.FINGER_MOVE_EVENT;
            finger.FingerID = (int)tcur.getSessionID();
            finger.Position.X = tcur.getX() * width;
            finger.Position.Y = tcur.getY() * height;
            finger.UserID = tcur.getCursorID();
            finger.DeviceName = this.GetType().ToString();

            fingerEvents.Add(finger);
        }

        public void removeTuioCursor(TuioCursor tcur)
        {
            //Console.WriteLine("del cur " + tcur.getCursorID() + " (" + tcur.getSessionID() + ")");

            if (!PreviewCursor(tcur))
                return;

            FingerEventArgs finger = new FingerEventArgs();
            finger.EventType = FingerEventType.FINGER_UP_EVENT;
            finger.FingerID = (int)tcur.getSessionID();
            finger.Position.X = tcur.getX() * width;
            finger.Position.Y = tcur.getY() * height;
            finger.UserID = tcur.getCursorID();
            finger.DeviceName = this.GetType().ToString();

            fingerEvents.Add(finger);
        }

        private bool PreviewCursor(TuioCursor tcur)
        {
            // There is noise in the left corner.
            // now just abandon this region
            if (tcur.getX() * width < 50 && tcur.getY() * height < 50)
                return false;
            return true;
        }

        public void refresh(TuioTime frameTime)
        {
            //Console.WriteLine("refresh "+frameTime.getTotalMilliseconds());

            MultiTouchEventArgs args = new MultiTouchEventArgs(fingerEvents, frameTime.getTotalMilliseconds());
            this.Dispatcher.BeginInvoke(new MultiTouchEventHandler(OnMultiTouch), new object[] { args });
            fingerEvents = new Collection<FingerEventArgs>();
        }

		#endregion

	}
}
